﻿#region Copyright(c) 2006 ZO, All right reserved.

// -----------------------------------------------------------------------------
// Copyright(c) 2006 ZO, All right reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   1.  Redistribution of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//   2.  Redistribution in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//   3.  The name of the author may not be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// -----------------------------------------------------------------------------

#endregion

#region Using directives

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
using ZO.SmartCore.Core;
using ZO.SmartCore.Helpers;
using ArgumentException = System.ArgumentException;
using ArgumentNullException = ZO.SmartCore.Core.ArgumentNullException;

#endregion

namespace ZO.SmartCore.Xml
{
    /// <summary>
    /// Represents an element. 
    /// </summary>
    public class Element : Node, IEquatable<Element>, ICloneable, ICloneable<Element>
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Element"/> class.
        /// </summary>
        /// <param name="name">Name of the element.</param>
        public Element(string name)
            : this(name, "", "", "", "")
        {
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="Element"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        public Element(string name, string value)
            : this(name, value, "", "", "")
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Element"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        /// <param name="defaultNamespace">The default Namespace of element.</param>
        public Element(string name, string value, string defaultNamespace)
            : this(name, value, "", "", defaultNamespace)
        {
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="Element"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        /// <param name="namespace">The namespace of element.</param>
        /// <param name="prefix">The prefix.</param>
        public Element(string name, string value, string @namespace, string prefix)
            : this(name, value, @namespace, prefix, "")
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Element"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        /// <param name="namespace">The namespace of element.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="defaultNamespace">the default namespace of element.</param>
        [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
        public Element(string name, string value, string @namespace, string prefix, string defaultNamespace)
        {
            if (String.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            } // if

            this._Name = name;
            this._Attributes = new AttributeList(this);

            if (!String.IsNullOrEmpty(prefix))
            {
                if (!String.IsNullOrEmpty(@namespace))
                {
                    this.Namespaces.Add(prefix, @namespace);
                    this.Prefix = prefix;
                }
            }


            if (!String.IsNullOrEmpty(value))
            {
                this._textValue = new Text(value);
                this.ChildNodes.Add(_textValue);
            }

            if (!String.IsNullOrEmpty(defaultNamespace))
            {
                this.Namespaces.DefaultNamespace = defaultNamespace;
            }
        }

        #endregion

        #region Destructor

        #endregion

        #region Fields

        private string _Name;

        private AttributeList _Attributes;
        private string _Prefix;
        private Text _textValue;

        #endregion

        #region Events

        #endregion

        #region Operators
        /// <summary>
        /// Operator ==s the specified left.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns></returns>
        public static bool operator ==(Element left, Element right)
        {

            if ((object)right == null)
                return ((object)left == null);
            return right.Equals(left);

        }

        /// <summary>
        /// Operator !=s the specified left.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns></returns>
        public static bool operator !=(Element left, Element right)
        {
            return !(left == right);
        }

    

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the <see cref="Element"/> with the specified name.
        /// </summary>
        /// <value></value>
        public Element this[string name]
        {
            get { return this.GetChildElement(name); } // get
            set
            {
                Element element = this.SelectElement(this, name, false);

                if (element == null)
                {
                    this.AddChildElement(value);
                }
                else
                {
                    this.ReplaceElement(value, element);
                } // else
            } // set
        } // this 


        /// <summary>
        /// Gets or sets the language.
        /// </summary>
        /// <value>The language.</value>
        /// <remarks>
        /// The language 'xml:lang' attribute  SHOULD be included by the initiating entity on the header for the initial stream 
        /// to specify the default language of any human-readable XML character data it sends over that stream. 
        /// If the attribute is included, the receiving entity SHOULD remember that value as the default for both the 
        /// initial stream and the response stream; if the attribute is not included, the receiving entity SHOULD use 
        /// a configurable default value for both streams, which it MUST communicate in the header for the response stream. 
        /// For all stanzas sent over the initial stream, if the initiating entity does not include an 'xml:lang' attribute, 
        /// the receiving entity SHOULD apply the default value; if the initiating entity does include an 'xml:lang' attribute, 
        /// the receiving entity MUST NOT modify or delete it (see also xml:langxml:lang). 
        /// The value of the 'xml:lang' attribute MUST conform to the format defined in RFC 3066 (Tags for the Identification of Languages, January 2001.[LANGTAGS]).
        /// </remarks>
        public XmlLang XmlLang
        {
            get
            {
                if (this.GetAttribute("xml:lang") == "English")
                {
                    return XmlLang.English;
                } // if

                return XmlLang.None;
            }
            set
            {
                if (value == XmlLang.None)
                {
                    this.Attributes.Remove("xml:lang");
                }
                else
                {
                    this.Attributes.Add("xml:lang", ReflectionHelper.GetDescription(value));
                } // else
            }
        }

        /// <summary>Gets or sets the markup representing only the child nodes of this node.</summary>
        /// <returns>The markup of the child nodes of this node.</returns>
        /// <exception cref="System.InvalidOperationException">Setting this property on a node that cannot have child nodes. </exception>
        /// <exception cref="System.Xml.XmlException">The XML specified when setting this property is not well-formed. </exception>
        public virtual string InnerXml
        {
            get
            {
                StringBuilder xml = new StringBuilder();
                if (this.ChildNodes.Count > 0)
                {
                    try
                    {
                        for (int index = 0; index < this.ChildNodes.Count; index++)
                        {
                            xml.Append(this.ChildNodes[index].ToString());
                        } // for
                    } // try
                    catch
                    {
                        throw;
                    } // catch
                } // if
                return xml.ToString();
            } // get
            set
            {
                Document doc = new Document();
                doc.LoadXml(value);
                Element root = doc.DocumentElement;
                if (root != null)
                {
                    this.ChildNodes.Clear();
                    this.AddChild(root);
                }
            }
        }


        /// <summary>Gets or sets the namespace prefix of this node.</summary>
        /// <returns>The namespace prefix of this node. If there is no prefix, this property returns String.Empty.</returns>
        /// <exception cref="System.Xml.XmlException">The specified prefix contains an invalid character.The specified prefix is malformed.The namespaceURI of this node is null.The specified prefix is "xml" and the namespaceURI of this node is different from http://www.w3.org/XML/1998/namespace. </exception>
        /// <exception cref="System.ArgumentException">This node is read-only </exception>
        public string Prefix
        {
            get { return this._Prefix; }
            set { this._Prefix = value; }
        }

        /// <summary>
        /// When overridden in a derived class, gets the type of the current node.
        /// </summary>
        /// <returns>One of the <see cref="NodeType"></see> values.</returns>
        public override NodeType NodeType
        {
            get { return NodeType.Element; }
        }

        /// <summary>
        /// Gets an Dictionary containing the list of attributes for this node. 
        /// </summary>
        public AttributeList Attributes
        {
            get { return this._Attributes; } // get
        }

        /// <summary>Gets the first child of NodeType element of the node.</summary>
        /// <returns>The first child of the node. If there is no such node, null is returned.</returns>
        public virtual Element FirstElement
        {
            get { return (Element)this.FindChild(NodeType.Element); }
        }

        /// <summary>Gets the first child of the node.</summary>
        /// <returns>The first child of the node. If there is no such node, null is returned.</returns>
        public virtual Node FirstChild
        {
            get
            {
                if (this.ChildNodes.Count > 0)
                {
                    return this.ChildNodes[0];
                }

                return null;
            }
        }

        /// <summary>Gets the qualified name of the node.</summary>
        /// <returns>The qualified name of the node. For XmlElement nodes, this is the tag name of the element.</returns>
        public virtual string Name
        {
            get { return this._Name; }
            //protected set
            //{
            //    _Name = value;
            //} // set
        }

        /// <summary>
        /// Is this Element a Rootnode?
        /// </summary>
        public bool IsDocumentElement
        {
            get { return (this.Parent != null ? false : true); }
        }

        /// <summary>Gets the last child of the node.</summary>
        /// <returns>The last child of the node. If there is no such node, null is returned.</returns>
        public virtual Node LastChild
        {
            get
            {
                if (this.ChildNodes.Count > 0)
                {
                    return this.ChildNodes[ChildNodes.Count - 1];
                } // if

                return null;
            }
        }

        /// <summary>
        /// Gets or sets the value of the node.
        /// </summary>
        /// <value></value>
        /// <returns>The value returned depends on the <see cref="NodeType"></see> of the node: Type Value Attribute The value of the attribute. CDATASection The content of the CDATA Section. Comment The content of the comment. Document null. DocumentFragment null. DocumentType null. Element null. You can use the <see cref="P:System.Xml.XmlElement.InnerText"></see> or <see cref="P:System.Xml.XmlElement.InnerXml"></see> properties to access the value of the element node. Entity null. EntityReference null. Notation null. ProcessingInstruction The entire content excluding the target. Text The content of the text node. SignificantWhitespace The white space characters. White space can consist of one or more space characters, carriage returns, line feeds, or tabs. Whitespace The white space characters. White space can consist of one or more space characters, carriage returns, line feeds, or tabs. XmlDeclaration The content of the declaration (that is, everything between &lt;?xml and ?&gt;). </returns>
        /// <exception cref="System.InvalidOperationException">Setting the value of a node that is not supposed to have a value (for example, an Element node). </exception>
        /// <exception cref="System.ArgumentException">Setting the value of a node that is read-only. </exception>
        public override string Value
        {
            get
            {
                if (_textValue != null)
                {
                    return _textValue.Value;
                }

                return "";
            }
            set
            {
                if (!String.IsNullOrEmpty(value))
                {
                    if (_textValue == null)
                    {
                        this._textValue = new Text(value);
                        this.ChildNodes.Add(_textValue);
                    }
                    else
                    {
                        this._textValue.Value = value;
                    }
                }
                else
                {
                    if (_textValue != null)
                    {
                        this.ChildNodes.Remove(_textValue);
                    }
                }
            }
        }



        #endregion

        #region Methods

        /// <summary>Gets a boolean value indicating whether the current node has any attributes.</summary>
        /// <returns>true if the current node has attributes; otherwise, false.</returns>
        public bool HasAttribute(string name)
        {
            return this.Attributes.Contains(name);
        }

        /// <summary>Adds the specified element to the end of the list of child nodes, of this element.</summary>
        /// <param name="newElement">The element to add</param>
        public Element AddChildElement(Element newElement)
        {
            this.AddChild(newElement);
            return newElement;
        }

        /// <summary>Adds the specified element to the end of the list of child nodes, of this element.</summary>
        /// <param name="name">The name of the element to add</param>
        public Element AddChildElement(string name)
        {
            if (String.IsNullOrEmpty(name))
            {
                return null;
            }

            Element newElement = new Element(name);

            this.AddChild(newElement);
            return newElement;
        }

        /// <summary>Adds the specified element to the end of the list of child nodes, of this element.</summary>
        /// <param name="name">The name of the element to add</param>
        /// <param name="value">The value of element.</param>
        public Element AddChildElement(string name, string value)
        {
            if (String.IsNullOrEmpty(name))
            {
                return null;
            }

            Element newElement = new Element(name, value);

            this.AddChild(newElement);
            return newElement;
        }

        /// <summary>
        /// Adds the specified element to the end of the list of child nodes, of this element.
        /// </summary>
        /// <param name="name">The name of the element to add</param>
        /// <param name="value">The value of element.</param>
        /// <param name="ns">The namespace of element.</param>
        public Element AddChildElement(string name, string value, string ns)
        {
            if (String.IsNullOrEmpty(name))
            {
                return null;
            }
            Element newElement = new Element(name, value, ns);

            this.AddChild(newElement);
            return newElement;
        }

        /// <summary>
        /// Gets the element from xml.
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns></returns>
        public static Element GetElement(string xml)
        {
            Document doc = new Document();
            doc.LoadXml(xml);
            Element root = doc.DocumentElement;
            return root;
        } // Load


        /// <summary>
        /// Adds the specified element to the end of the list of child nodes, of this element.
        /// </summary>
        /// <param name="element">The element to add.</param>
        /// <returns></returns>
        public T AddChildElement<T>(T element) where T : Element
        {
            if (element != null)
            {
                this.AddChild(element);
                return element;
            }

            return element;
        }

        /// <summary>
        /// Adds the specified node to the end of the list of child nodes, of this node.
        /// </summary>
        /// <param name="newChild">The node to add</param>
        public override void AddChild(Node newChild)
        {
            if (newChild == null)
            {
                return;
            }

            switch (newChild.NodeType)
            {
                case NodeType.Document:
                    break;

                case NodeType.Element:
                case NodeType.Comment:
                    base.AddChild(newChild);
                    break;

                case NodeType.Attribute:
                    this.Attributes.Add(newChild as Attribute);
                    break;

                case NodeType.Text:
                    this.Value = newChild.Value;
                    break;
                case NodeType.CDATA:
                    base.AddChild(newChild);
                    break;

                default:
                    break;
            }
        }

        /// <summary>Remove the specified element from the list of child nodes, of this element.</summary>
        /// <param name="element">The element to Remove</param>
        /// <remarks>it only removes child of current element</remarks>
        public virtual Element RemoveChildElement(Element element)
        {
            this.RemoveChild(element);
            return element;
        }

        /// <summary>Remove the specified element from the list of child nodes, of this element.</summary>
        /// <param name="name">The name of the element to Remove</param>
        /// <remarks>it only removes child of current element</remarks>
        public virtual Element RemoveChildElement(string name)
        {
            bool flag = false;

            if (String.IsNullOrEmpty(name))
            {
                return null;
            }

            Element element = null;
            foreach (Node node in this.ChildNodes)
            {
                if (node.NodeType == NodeType.Element)
                {
                    element = node as Element;

                    if (element != null)
                    {
                        if (element.Name == name)
                        {
                            flag = true;
                            break;
                        }
                    }
                }
            }

            if (flag)
            {
                this.RemoveChild(element);
                return element;
            }

            return null;
        }

        /// <summary>
        /// Remove the specified element from the list of child nodes, of this element.
        /// </summary>
        /// <remarks>it only removes child of current element</remarks>
        public virtual T RemoveChildElement<T>(T element) where T : Element
        {
            if (element != null)
            {
                bool flag = false;

                Element currentElement = null;

                foreach (Node node in this.ChildNodes)
                {
                    if (node.NodeType == NodeType.Element)
                    {
                        currentElement = (Element)node;

                        if (element.Equals(currentElement))
                        {
                            flag = true;
                            break;
                        }
                    }
                }

                if (flag)
                {
                    this.RemoveChild(currentElement);
                    return element;
                }
            }
            return default(T);
        }

        /// <summary>
        /// Remove the first specified element from the list of child nodes, of this element.
        /// </summary>
        /// <remarks>it only removes child of current element</remarks>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public virtual T RemoveChildElement<T>() where T : Element
        {
            bool flag = false;

            Element currentElement;
            T typeElement = null;
            foreach (Node node in this.ChildNodes)
            {
                if (node.NodeType == NodeType.Element)
                {
                    currentElement = (Element)node;

                    typeElement = Activator.CreateInstance<T>();

                    if (typeElement.Equals(currentElement))
                    {
                        flag = true;
                        break;
                    }
                }
            }

            if (flag)
            {
                this.RemoveChild(typeElement);
                return typeElement;
            }
            return null;
        }

        /// <summary>
        /// Replaces the child element with new child element.
        /// </summary>
        /// <param name="newChild">The new child.</param>
        /// <param name="oldChild">The old child.</param>
        /// <remarks>if old child is not exists, new element is added to the list.</remarks>
        public void ReplaceElement(Element newChild, Element oldChild)
        {
            if (oldChild != null)
            {
                if (this.ChildNodes.Contains(oldChild))
                {
                    if (newChild == null)
                    {
                        return;
                    } // if
                    else
                    {
                        int index = this.ChildNodes.IndexOf(oldChild);

                        this.ChildNodes.Insert(index, newChild);
                    } // else
                } // if
            } // if

            if (newChild != null)
            {
                this.ChildNodes.Add(newChild);
            }
        }

        // ReplaceChild(newChild, oldChild)


        /// <summary>
        /// Replaces the existing child element with same element name.
        /// </summary>
        /// <param name="newChild">The new child.</param>
        public void ReplaceElement(Element newChild)
        {
            if (newChild == null)
            {
                return;
            } // if


            for (int index = 0; index < this.ChildNodes.Count; index++)
            {
                Node node = this.ChildNodes[index];

                if (node.NodeType == NodeType.Element)
                {
                    if (((Element)node).Name == newChild.Name)
                    {
                        this.ChildNodes.Insert(index, newChild);
                    } // if
                    // if
                } // if
                // if                    
            } // for (index)
        } // ReplaceChild(newChild, oldChild)


        /// <summary>
        /// Selects the first Element that matches the specified name. 
        /// </summary>
        /// <param name="name">name of the element</param>
        /// <returns>The first Element that matches or a null reference.</returns>
        public Element SelectSingleElement(string name)
        {
            return this.SelectElement(this, name, false);
        } // SelectSingleElement

        /// <summary>
        /// Selects the first Element that matches the specified name.
        /// </summary>
        /// <param name="name">name of the element</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>
        /// The first Element that matches or a null reference.
        /// </returns>
        public Element SelectSingleElement(string name, bool traverseChildren)
        {
            return this.SelectElement(this, name, traverseChildren);
        } // SelectSingleElement


        /// <summary>
        /// Selects the element with specified name, attribute and value.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="elementName">Name of the element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <param name="attributeValue">The attribute value.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>
        /// The first Element that matches or a null reference.
        /// </returns>
        private Element SelectElement(Node node, string elementName, string attributeName, string attributeValue,
                                      bool traverseChildren)
        {
            Element tempElement = null;

            if (node.NodeType == NodeType.Element)
            {
                Element element2 = node as Element;
                if (((element2.Name == elementName) && element2.HasAttribute(attributeName)) &&
                    (element2.GetAttribute(attributeName) == attributeValue))
                {
                    return element2;
                }
            }

            if (node.ChildNodes.Count > 0)
            {
                foreach (Node currentNode in node.ChildNodes)
                {
                    if (currentNode.NodeType == NodeType.Element)
                    {
                        Element element2 = currentNode as Element;
                        if (((element2.Name == elementName) && element2.HasAttribute(attributeName)) &&
                            (element2.GetAttribute(attributeName) == attributeValue))
                        {
                            return element2;
                        }
                    }

                    if (traverseChildren)
                    {
                        tempElement = this.SelectElement(currentNode, elementName, attributeName, attributeValue, true);
                        if (tempElement != null)
                        {
                            return tempElement;
                        } // if
                    }
                    // if
                }
            }
            return tempElement;
        }


        /// <summary>
        /// Selects the first Element that matches the specified name and namespace.
        /// </summary>
        /// <param name="name">name of the element</param>
        /// <param name="ns">The namespace to match.</param>
        /// <returns>
        /// The first Element that matches or a null reference.
        /// </returns>
        public Element SelectSingleElement(string name, string ns)
        {
            return this.SelectElement(this, name, "xmlns", ns, false);
        } // SelectSingleElement


        /// <summary>
        /// Selects the single element.
        /// </summary>
        /// <returns>The first Element that matches or a null reference.</returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public T SelectSingleElement<T>() where T : Element, new()
        {
            return this.SelectElement<T>(this, false);
        }

        /// <summary>
        /// Selects the single element.
        /// </summary>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns> The first Element that matches or a null reference.</returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public T SelectSingleElement<T>(bool traverseChildren) where T : Element, new()
        {
            return this.SelectElement<T>(this, traverseChildren);
        }


        /// <summary>
        /// Selects the first Element that matches the specified name and namespace.
        /// </summary>
        /// <param name="name">name of the element</param>
        /// <param name="ns">The namespace to match.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>
        /// The first Element that matches or a null reference.
        /// </returns>
        public Element SelectSingleElement(string name, string ns, bool traverseChildren)
        {
            return this.SelectElement(this, name, "xmlns", ns, traverseChildren);
        } // SelectSingleElement


        /// <summary>
        /// Selects the single element with specified name, attribute.
        /// </summary>
        /// <param name="name">Name of the element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <param name="attributeValue">The attribute value.</param>
        /// <returns>
        /// The first Element that matches or a null reference.
        /// </returns>
        public Element SelectSingleElement(string name, string attributeName, string attributeValue)
        {
            return this.SelectElement(this, name, attributeName, attributeValue, false);
        } // SelectSingleElement

        /// <summary>
        /// Selects the single element with specified name, attribute.
        /// </summary>
        /// <param name="name">Name of the element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <param name="attributeValue">The attribute value.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>
        /// The first Element that matches or a null reference.
        /// </returns>
        public Element SelectSingleElement(string name, string attributeName, string attributeValue,
                                           bool traverseChildren)
        {
            return this.SelectElement(this, name, attributeName, attributeValue, traverseChildren);
        } // SelectSingleElement


        /// <summary>
        /// Selects the element.
        /// </summary>
        /// <param name="node">The node to select.</param>
        /// <param name="name">The name of the element to select.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns></returns>
        private Element SelectElement(Node node, string name, bool traverseChildren)
        {
            Element tempElement = null;
            if (node.ChildNodes.Count > 0)
            {
                foreach (Node currentNode in node.ChildNodes)
                {
                    if (currentNode.NodeType == NodeType.Element)
                    {
                        Element tempNode = (Element)currentNode;

                        if (tempNode.Name == name)
                        {
                            //if(!string.IsNullOrEmpty(tempNode.Prefix))
                            //{
                            //    Node parentNode = tempNode.Parent;

                            //    if(parentNode != null)
                            //    {
                            //        string ns = parentNode.Namespaces.LookupNamespace(tempNode.Prefix);

                            //        if(!string.IsNullOrEmpty(tempNode.Prefix))
                            //        {
                            //            tempNode.Namespaces.Add(tempNode.Prefix, ns);
                            //        }

                            //    }
                            //}
                            return tempNode;
                        }
                        if (traverseChildren)
                        {
                            tempElement = this.SelectElement(tempNode, name, true);
                            if (tempElement != null)
                            {
                                return tempElement;
                            }
                        }
                    }
                }
            }
            return tempElement;
        }

        // SelectElement(node, name, traverseChildren)


        /// <summary>
        /// Selects the element.
        /// </summary>
        /// <param name="node">The node to select.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns></returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        private T SelectElement<T>(Node node, bool traverseChildren) where T : Element, new()
        {
            if (node.ChildNodes.Count > 0)
            {
                foreach (Node currentNode in node.ChildNodes)
                {
                    if (currentNode.NodeType == NodeType.Element)
                    {
                        Element currentElement = (Element)currentNode;

                        T typeElement = Activator.CreateInstance<T>();


                        if (typeElement.Equals(currentElement))
                        {
                            Object[] args = new object[1];
                            args[0] = currentElement.ToString();

                            ReflectionHelper.CallMethod(typeElement, "LoadXml", args);
                            return typeElement;
                        }
                        if (traverseChildren)
                        {
                            return this.SelectElement<T>(currentNode, true);
                        }
                    }
                }
            }
            return default(T);
        }


        /// <summary>
        /// Selects the elements.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="name">The name.</param>
        /// <param name="elementList">The element list.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        private void SelectElements(Element element, string name, List<Element> elementList, bool traverseChildren)
        {
            if (element.ChildNodes.Count > 0)
            {
                foreach (Node currentNode in element.ChildNodes)
                {
                    if (currentNode.NodeType == NodeType.Element)
                    {
                        Element tempElement = (Element)currentNode;
                        if (tempElement.Name == name)
                        {
                            elementList.Add(tempElement);
                        }
                        if (traverseChildren)
                        {
                            this.SelectElements(tempElement, name, elementList, true);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Selects the elements.
        /// </summary>
        /// <param name="node">The node to Search.</param>
        /// <param name="elementList">The element list.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        private void SelectElements<T>(Node node, List<T> elementList, bool traverseChildren) where T : Element, new()
        {
            if (node.ChildNodes.Count > 0)
            {
                foreach (Node currentNode in node.ChildNodes)
                {
                    if (currentNode.NodeType == NodeType.Element)
                    {
                        Element currentElement = (Element)currentNode;

                        T typeElement = Activator.CreateInstance<T>();

                        if (typeElement.Equals(currentElement))
                        {
                            Object[] args = new object[1];
                            args[0] = currentElement.ToString();

                            ReflectionHelper.CallMethod(typeElement, "LoadXml", args);

                            elementList.Add(typeElement);
                        }
                        if (traverseChildren)
                        {
                            this.SelectElements<T>(currentElement, elementList, true);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Selects a list of elements matching the name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>containing a collection of elements matched.</returns>
        public ReadOnlyCollection<Element> SelectElements(string name)
        {
            List<Element> list = new List<Element>();
            this.SelectElements(this, name, list, false);

            return new ReadOnlyCollection<Element>(list);
        }


        /// <summary>
        /// Selects a list of elements matching the type.
        /// </summary>
        /// <returns>
        /// containing a collection of elements matched.
        /// </returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public ReadOnlyCollection<T> SelectElements<T>() where T : Element, new()
        {
            List<T> list = new List<T>();
            this.SelectElements<T>(this, list, false);

            return new ReadOnlyCollection<T>(list);
        }

        /// <summary>
        /// Selects a list of elements matching the name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>
        /// containing a collection of elements matched.
        /// </returns>
        public ReadOnlyCollection<Element> SelectElements(string name, bool traverseChildren)
        {
            List<Element> list = new List<Element>();
            this.SelectElements(this, name, list, traverseChildren);

            return new ReadOnlyCollection<Element>(list);
        }

        /// <summary>
        /// Selects a list of elements matching the name.
        /// </summary>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>
        /// containing a collection of elements matched.
        /// </returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public ReadOnlyCollection<T> SelectElements<T>(bool traverseChildren) where T : Element, new()
        {
            List<T> list = new List<T>();
            this.SelectElements<T>(this, list, traverseChildren);

            return new ReadOnlyCollection<T>(list);
        }


        /// <summary>
        /// Determines whether the Element contains element with specified name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>
        /// 	<c>true</c> if the specified name has element; otherwise, <c>false</c>.
        /// </returns>
        /// <overloads>
        /// Get whether Element Contains child element.
        /// </overloads>
        public bool HasChildElement(string name)
        {
            if (String.IsNullOrEmpty(name))
            {
                return false;
            }

            return this.HasChildElement(name, false);
        } // HasElement

        /// <summary>
        /// Determines whether the Element contains element with specified name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>
        /// 	<c>true</c> if the specified name has element; otherwise, <c>false</c>.
        /// </returns>
        public bool HasChildElement(string name, bool traverseChildren)
        {
            if (String.IsNullOrEmpty(name))
            {
                return false;
            }

            Element tag = this.SelectElement(this, name, traverseChildren);

            if (tag != null)
            {
                return true;
            } // if

            return false;
        } // HasElement

        /// <summary>
        /// Determines whether the Element contains element with specified name.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if the specified type name has element; otherwise, <c>false</c>.
        /// </returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public bool HasChildElement<T>() where T : Element, new()
        {
            return this.HasChildElement<T>(false);
        } // HasElement

        /// <summary>
        /// Determines whether the Element contains element with specified name.
        /// </summary>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>
        /// 	<c>true</c> if the specified name has element; otherwise, <c>false</c>.
        /// </returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public bool HasChildElement<T>(bool traverseChildren) where T : Element, new()
        {
            T tag = this.SelectElement<T>(this, traverseChildren);

            if (tag != null)
            {
                return true;
            } // if

            return false;
        } // HasElement


        /// <summary>
        /// Get the Element with specified name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>Child Element With Specified Name, else null</returns>
        /// <overloads>
        /// Get The Child Element.
        /// </overloads>
        public Element GetChildElement(string name)
        {
            if (String.IsNullOrEmpty(name))
            {
                return null;
            }

            return this.GetChildElement(name, false);
        } // GetElement


        /// <summary>
        /// Get the Element Value with specified name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>Child Element Value With Specified Name, else null</returns>
        public string GetChildElementValue(string name)
        {
            Element element = GetChildElement(name, false);

            if (element == null)
            {
                return String.Empty;
            }

            return element.Value;
        } // GetElement


        /// <summary>
        /// Set the Element Value with specified name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        public void SetChildElementValue(string name, string value)
        {
            if (!this.HasChildElement(name))
            {
                this.AddChild(new Element(name, value));
            }
            else
            {
                this.SelectSingleElement(name).Value = value;
            }
        } // GetElement


        /// <summary>
        /// Get the Element with specified name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>Child Element With Specified Name, else null</returns>
        public Element GetChildElement(string name, bool traverseChildren)
        {
            if (String.IsNullOrEmpty(name))
            {
                return null;
            }

            Element tag = this.SelectElement(this, name, traverseChildren);

            if (tag != null)
            {
                return tag;
            } // if

            return null;
        } // GetElement

        /// <summary>
        /// Get the Element with specified type.
        /// </summary>
        /// <returns>Child Element With Specified type, else null</returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public T GetChildElement<T>() where T : Element, new()
        {
            return this.GetChildElement<T>(false);
        } // GetElement


        /// <summary>
        /// Gets theElement as enum.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="enumType">Type of the enum.</param>
        public virtual object GetChildElementAsEnum(string name, Type enumType)
        {
            Element element1 = this.SelectElement(this, name, false);
            string value = "";

            if (element1 != null)
            {
                value = element1.Value;
            }

            if (String.IsNullOrEmpty(value))
            {
                return -1;
            }

            try
            {
                return Enum.Parse(enumType, value, true);
            } // try
            catch (ArgumentException)
            {
                return -1;
            } // catch
        } // GetAttributeAsEnum

        /// <summary>
        /// Get the Element with specified type.
        /// </summary>
        /// <param name="traverseChildren">if set to <c>true</c> [traverse children].</param>
        /// <returns>Child Element With Specified type, else null</returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public T GetChildElement<T>(bool traverseChildren) where T : Element, new()
        {
            T tag = this.SelectElement<T>(this, traverseChildren);

            return tag;
        } // GetElement

        /// <summary>
        /// Loads the XML into current Elemnt.
        /// </summary>
        /// <param name="xml">The XML.</param>
        [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
        public void LoadXml(string xml)
        {
            Element root = GetElement(xml);

            if (root.Equals(this))
            {
                this.Attributes.Clear();
                this.ChildNodes.Clear();
                this.Namespaces.Clear();

                foreach (Attribute attrib in root.Attributes.Values)
                {
                    this.Attributes.Add(attrib);
                }

                //this.Namespaces.DefaultNamespace = root.Namespaces.DefaultNamespace;

                IEnumerator<KeyValuePair<string, string>> enumerator = root.Namespaces.GetEnumerator();

                while (enumerator.MoveNext())
                {
                    this.Namespaces.Add(enumerator.Current.Key, enumerator.Current.Value);
                }

                //this.Prefix = root.Prefix;
                this.XmlLang = root.XmlLang;
                foreach (Node child in root.ChildNodes)
                {
                    this.ChildNodes.Add(child);
                }
            }
        } // Load


        /// <summary>
        /// Loads the XML Document into current Elemnt.
        /// </summary>
        /// <param name="xmlDoc">The XML document to load.</param>
        [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
        public void Load(Document xmlDoc)
        {
            if (xmlDoc == null)
            {
                return;
            }

            Element root = xmlDoc.DocumentElement;


            if (root.Equals(this))
            {
                this.Attributes.Clear();
                this.ChildNodes.Clear();
                this.Namespaces.Clear();

                foreach (Attribute attrib in root.Attributes.Values)
                {
                    this.Attributes.Add(attrib);
                }

                //this.Namespaces.DefaultNamespace = root.Namespaces.DefaultNamespace;

                IEnumerator<KeyValuePair<string, string>> enumerator = root.Namespaces.GetEnumerator();

                while (enumerator.MoveNext())
                {
                    this.Namespaces.Add(enumerator.Current.Key, enumerator.Current.Value);
                }

                //this.Prefix = root.Prefix;
                this.XmlLang = root.XmlLang;
                foreach (Node child in root.ChildNodes)
                {
                    this.ChildNodes.Add(child);
                }
            }
        } // Load


        /// <summary>
        /// Loads the XML element into current Elemnt.
        /// </summary>
        /// <param name="element">The element to load.</param>
        [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
        public void Load(Element element)
        {
            if (element == null)
            {
                return;
            }

            Element root = element;


            if (root.Equals(this))
            {
                this.Attributes.Clear();
                this.ChildNodes.Clear();
                this.Namespaces.Clear();

                foreach (Attribute attrib in root.Attributes.Values)
                {
                    this.Attributes.Add(attrib);
                }

                //this.Namespaces.DefaultNamespace = root.Namespaces.DefaultNamespace;

                IEnumerator<KeyValuePair<string, string>> enumerator = root.Namespaces.GetEnumerator();

                while (enumerator.MoveNext())
                {
                    this.Namespaces.Add(enumerator.Current.Key, enumerator.Current.Value);
                }

                //this.Prefix = root.Prefix;
                this.XmlLang = root.XmlLang;
                foreach (Node child in root.ChildNodes)
                {
                    this.ChildNodes.Add(child);
                }
            }
        } // Load

        /// <summary>
        /// Serves as a hash function for a particular type. <see cref="M:System.Object.GetHashCode"></see> is suitable for use in hashing algorithms and data structures like a hash table.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override int GetHashCode()
        {
            int hashCode = ((String.IsNullOrEmpty(this.Name) ? 0 : this.Name.GetHashCode()) +
                            (String.IsNullOrEmpty(this.Namespaces.DefaultNamespace)
                                 ? 0
                                 : this.Namespaces.DefaultNamespace.GetHashCode()) +
                            (String.IsNullOrEmpty(this.Prefix) ? 0 : this.Prefix.GetHashCode()));

            string prefix = this.Namespaces.LookupNamespace(this.Prefix);

            if (!String.IsNullOrEmpty(prefix))
            {
                hashCode += prefix.GetHashCode();
            }

            Console.WriteLine(hashCode.ToString(CultureInfo.CurrentCulture));
            return hashCode;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"></see> to compare with the current <see cref="T:System.Object"></see>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>; otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            return this.Equals(obj as Element);
        }

        #endregion

        #region Attributes Methods

        /// <summary>
        /// Sets the integer value as attribute.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        public void SetAttributeAsInteger(string key, int value)
        {
            this.Attributes.Add(key, value.ToString(CultureInfo.InvariantCulture));
        } // Add

        /// <summary>
        /// Sets the double value as attribute.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        public void SetAttributeAsDouble(string key, double value)
        {
            NumberFormatInfo info = new NumberFormatInfo();
            info.NumberGroupSeparator = ".";
            this.SetAttributeAsDouble(key, value, info);
        } // Add

        /// <summary>
        /// Sets the double value as attribute.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        /// <param name="formatProvider">The format provider.</param>
        public void SetAttributeAsDouble(string key, double value, IFormatProvider formatProvider)
        {
            this.Attributes.Add(key, value.ToString(formatProvider));
        } // Add


        /// <summary>
        /// Sets the boolean value as attribute.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        public void SetAttributeAsBoolean(string key, bool value)
        {
            this.Attributes.Add(key, value.ToString());
        } // Add

        /// <summary>
        /// Get a Attribute of type double (Decimal seperator = ".")
        /// </summary>
        /// <param name="name">The name.</param>
        public virtual double GetAttributeAsDouble(string name)
        {
            // Parse the double always in english format ==> "." = Decimal seperator
            NumberFormatInfo nfi = new NumberFormatInfo();
            nfi.NumberGroupSeparator = ".";
            return GetAttributeAsDouble(name, nfi);
        }

        /// <summary>
        /// Get a Attribute of type Boolean
        /// </summary>
        /// <param name="name">The name.</param>
        public virtual bool GetAttributeAsBoolean(string name)
        {
            string attr = this.GetAttribute(name);

            if (!String.IsNullOrEmpty(attr))
            {
                bool result;

                if (Boolean.TryParse(attr, out result))
                {
                    return result;
                } // if
            } // if

            return false;
        }


        /// <summary>
        /// Gets the attribute as double.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="format">The format.</param>
        public virtual double GetAttributeAsDouble(string name, IFormatProvider format)
        {
            string attr = this.GetAttribute(name);

            if (!String.IsNullOrEmpty(attr))
            {
                double result;

                if (Double.TryParse(attr, NumberStyles.Any, format, out result))
                {
                    return result;
                } // if
            } // if
            return Double.NaN;
        }


        /// <summary>
        /// Gets the attribute as integer.
        /// </summary>
        /// <param name="name">The name.</param>
        public virtual int GetAttributeAsInteger(string name)
        {
            string attr = this.GetAttribute(name);

            if (!String.IsNullOrEmpty(attr))
            {
                int result;
                if (Int32.TryParse(attr, out result))
                {
                    return result;
                }
            }
            return 0;
        }

        /// <summary>
        /// Gets the attribute as enum.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="enumType">Type of the enum.</param>
        public virtual object GetAttributeAsEnum(string name, Type enumType)
        {
            string attr = this.GetAttribute(name);

            if (String.IsNullOrEmpty(attr))
            {
                return -1;
            }

            try
            {
                return Enum.Parse(enumType, attr, true);
            } // try
            catch (ArgumentException)
            {
                return -1;
            } // catch
        } // GetAttributeAsEnum

        /// <summary>
        /// Gets the attribute.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public virtual string GetAttribute(string name)
        {
            if (this.Attributes.Contains(name))
            {
                return this.Attributes[name];
            }
            return String.Empty;
        }

        /// <summary>
        /// Sets the attribute as enum.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="enumValue">The enum value.</param>
        public void SetAttributeAsEnum(string name, Enum enumValue)
        {
            if (enumValue == null)
            {
                return;
            }
            this.Attributes[name] = enumValue.ToString();
        } // GetAttributeAsEnum

        /// <summary>
        /// Sets the attribute.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        public void SetAttribute(string name, string value)
        {
            this.Attributes[name] = value;
        }

        /// <summary>
        /// Removes a Attribute
        /// </summary>
        /// <param name="name">Attribute as string to remove</param>
        public void RemoveAttribute(string name)
        {
            if (this.Attributes.Contains(name))
            {
                this.Attributes.Remove(name);
                return;
            }
        }

        #endregion

        #region IEquatable<Element> Members

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the other parameter; otherwise, false.
        /// </returns>
        public virtual bool Equals(Element other)
        {
            if (other != null)
            {
                if ((other.Name == this.Name) &&
                    (other.Namespaces.DefaultNamespace == this.Namespaces.DefaultNamespace))
                {
                    if (this.Prefix != null)
                    {
                        if (other.Namespaces.Contains(this.Prefix, true))
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                    return true;
                }
            }


            return false;
        }

        #endregion

        #region ICloneable Members

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        object ICloneable.Clone()
        {
            return this.Clone();
        }

        #endregion

        #region ICloneable<Element> Members

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public Element Clone()
        {
            Document doc = new Document();
            doc.LoadXml(this.ToString(true));
            Element newElement = doc.DocumentElement;
            return newElement;
        }

        #endregion
    }
}